package com.chance.cc.crawler.development.scripts.douyin.api.custom;

import com.alibaba.fastjson.JSON;
import com.chance.cc.crawler.core.CrawlerEnum;
import com.chance.cc.crawler.core.CrawlerRecordContext;
import com.chance.cc.crawler.core.downloader.HttpPage;
import com.chance.cc.crawler.core.filter.FilterInfo;
import com.chance.cc.crawler.core.pipeline.result.CrawlerData;
import com.chance.cc.crawler.core.record.CrawlerRecord;
import com.chance.cc.crawler.core.record.CrawlerRequestRecord;
import com.chance.cc.crawler.core.script.crawler.CrawlerCommonScript;
import com.chance.cc.crawler.core.selector.Json;
import com.chance.cc.crawler.core.tags.KVTag;
import com.chance.cc.crawler.core.tags.crawler.CrawlerBusinessTags;
import com.chance.cc.crawler.meta.core.bean.common.MetaResponse;
import com.chance.cc.crawler.meta.core.bean.crawler.CrawlerDomainKeys;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.chance.cc.crawler.core.CrawlerEnum.CrawlerRequestType.filter;
import static com.chance.cc.crawler.development.scripts.allfeild.AICCommonField.*;
import static java.lang.Math.abs;

/**
 * @author bx
 * @date 2020/12/6 0006 12:52
 */
public class DYAccountCustomCrawlerScript extends CrawlerCommonScript {

    private static Logger logger = LoggerFactory.getLogger(DYAccountCustomCrawlerScript.class);
    public static final String site = "api-account-custom";

    public static final String accountUrlRegular = "https://api5-core-c-lf.amemv.com/aweme/v1/aweme/post/\\S*";
    public static final String accountUrl = "https://api5-core-c-lf.amemv.com/aweme/v1/aweme/post/?";
    public static final String accountUrlParamFormat = "source=0&user_avatar_shrink=144_144&video_cover_shrink=372_495&publish_video_strategy_type=2" +
            "&max_cursor=%s" +
            "&sec_user_id=%s" +
            "&show_live_replay_strategy=1&count=20&is_order_flow=0&page_from=2&location_permission=true" +
            "&_rticket=%s" +
            "&cpu_support64=true&host_abi=arm64-v8a&appTheme=dark&minor_status=0&oaid=70fc03ff20a0922c&ac=wifi" +
            "&ts=%s&";

    public static final String commentUrl = "https://api5-normal-c-lf.amemv.com/aweme/v2/comment/list/?";
    public static final String commentUrlParamFormat = "aweme_id=%s" +
            "&cursor=%s" +
            "&count=20&address_book_access=2&gps_access=1&forward_page_type=1&channel_id=23&city=310000&hotsoon_filtered_count=0&hotsoon_has_more=0&follower_count=0&is_familiar=0&page_source=0&user_avatar_shrink=96_96" +
            "&aweme_author=%s" +
            "&item_type=0&manifest_version_code=150301" +
            "&_rticket=%s" +
            "&app_type=normal" +
            "&iid=3466115461681679" +
            "&channel=xiaomi_1128_64&device_type=MI+5s&language=zh&cpu_support64=true&host_abi=arm64-v8a&resolution=1080*1920" +
            "&openudid=e4fc5a45e14abe0f" +
            "&update_version_code=15309900" +
            "&cdid=7ec19547-0744-4956-9e21-132f834a1900" +
            "&appTheme=dark&minor_status=0&os_api=26&dpi=480" +
            "&oaid=379e0a4c7f10a148" +
            "&ac=wifi" +
            "&device_id=40256406202" +
            "&os_version=8.0.0&version_code=150300&app_name=aweme&version_name=15.3.0&device_brand=Xiaomi&ssmix=a&device_platform=android&aid=1128" +
            "&ts=%s";

    public static final String byteTable = "D6 28 3B 71 70 76 BE 1B A4 FE 19 57 5E 6C BC 21 B2 14 37 7D 8C A2 FA 67 55 6A 95 E3 FA 67 78 ED 8E 55 33 89 A8 CE 36 B3 5C D6 B2 6F 96 C4 34 B9 6A EC 34 95 C4 FA 72 FF B8 42 8D FB EC 70 F0 85 46 D8 B2 A1 E0 CE AE 4B 7D AE A4 87 CE E3 AC 51 55 C4 36 AD FC C4 EA 97 70 6A 85 37 6A C8 68 FA FE B0 33 B9 67 7E CE E3 CC 86 D6 9F 76 74 89 E9 DA 9C 78 C5 95 AA B0 34 B3 F2 7D B2 A2 ED E0 B5 B6 88 95 D1 51 D6 9E 7D D1 C8 F9 B7 70 CC 9C B6 92 C5 FA DD 9F 28 DA C7 E0 CA 95 B2 DA 34 97 CE 74 FA 37 E9 7D C4 A2 37 FB FA F1 CF AA 89 7D 55 AE 87 BC F5 E9 6A C4 68 C7 FA 76 85 14 D0 D0 E5 CE FF 19 D6 E5 D6 CC F1 F4 6C E9 E7 89 B2 B7 AE 28 89 BE 5E DC 87 6C F7 51 F2 67 78 AE B3 4B A2 B3 21 3B 55 F8 B3 76 B2 CF B3 B3 FF B3 5E 71 7D FA FC FF A8 7D FE D8 9C 1B C4 6A F9 88 B5 E5";
    public static final String NULL_MD5_STRING = "00000000000000000000000000000000";

    @Override
    public List<CrawlerRequestRecord> parseLinks(CrawlerRequestRecord crawlerRecord, HttpPage page) {
        List<CrawlerRequestRecord> crawlerRequestRecords = new ArrayList<>();
        if (!page.isDownloadSuccess()){
            logger.error("dy custom account video request download has error status code {},page raw text [{}],will retry!",
                    page.getStatusCode(),page.getRawText());
            addCrawlerRecords(crawlerRequestRecords,crawlerRecord);
            crawlerRecord.setNeedWashPage(false); //下载异常，不需要清洗页面
            return crawlerRequestRecords;
        }

        try{
            String keyword = crawlerRecord.tagsCreator().bizTags().getCategoryTag().getKVTagStrVal("sec_uid");

            Json accountResultJson = new Json(page.getRawText());
            String statusCode = accountResultJson.jsonPath($_type + ".status_code").get();
            String hasMore = accountResultJson.jsonPath($_type + ".has_more").get();

            long lastReaseTime = 0;
            FilterInfo filterInfo = crawlerRecord.getFilterInfos().get(0);
            long startTime = filterInfo.getDateAllowRange()[0];
            long endTime = filterInfo.getDateAllowRange()[1];

            List<String> awemeList = accountResultJson.jsonPath($_type + ".aweme_list").all();
            if (awemeList != null && awemeList.size() > 0){
                for (String aweme : awemeList) {
                    Json awemeJson = new Json(aweme);
                    String awemeId = awemeJson.jsonPath($_type + ".aweme_id").get();
                    String create_time = awemeJson.jsonPath($_type + ".create_time").get();
                    lastReaseTime = new Long(create_time) * 1000L;
                    Integer commentCount = Integer.valueOf(awemeJson.jsonPath($_type + ".statistics.comment_count").get());
                    if (lastReaseTime >= startTime && lastReaseTime <= endTime && commentCount > 0){
                        if (!crawlerRecord.tagsCreator().bizTags().hasKVTag("comment_record_filter_info")) {
                            logger.error("dy custom account video comment crawler need to filter information!");
                            continue;
                        }
                        KVTag filterInfoTag = crawlerRecord.tagsCreator().bizTags().getCategoryTag().getKVTag("comment_record_filter_info");
                        CrawlerRecord filterInfoRecord = JSON.parseObject((String) filterInfoTag.getVal(), CrawlerRecord.class);
                        long rticket = System.currentTimeMillis();
                        long ts = rticket/1000;
                        String urlParam = String.format(commentUrlParamFormat,awemeId, 0, keyword, rticket, ts);
                        String xgorgon = xGorgon(ts, strToByte(getXGon(urlParam, "", "")));

                        CrawlerRequestRecord crawlerRequestRecord = CrawlerRequestRecord.builder()
                                .turnPageRequest(crawlerRecord)
                                .httpUrl(commentUrl + urlParam)
                                .httpHead("User-Agent", "com.ss.android.ugc.aweme/150301 (Linux; U; Android 8.0.0; zh_CN; MI 5s; Build/OPR1.170623.032; Cronet/TTNetVersion:539f4bcf 2021-01-18 QuicVersion:47946d2a 2020-10-14)")
                                .httpHead("X-Gorgon", xgorgon)
                                .httpHead("X-Khronos", ""+ts)
                                .httpHead("Connection", "Keep-Alive")
                                .httpHead("Host", "api5-normal-c-lf.amemv.com")
                                .releaseTime(System.currentTimeMillis())
                                .needWashed(true)
                                .notFilterRecord()
                                .copyBizTags()
                                .build();
                        crawlerRequestRecord.tagsCreator().bizTags().addSite("api-comment");
                        crawlerRequestRecord.setFilter(filterInfoRecord.getFilter());
                        crawlerRequestRecord.setFilterInfos(filterInfoRecord.getFilterInfos());
                        crawlerRequestRecord.setTurnPageFilterInfo(filterInfoRecord.getTurnPageFilterInfo());
                        crawlerRequestRecord.tagsCreator().bizTags().addCustomKV("aweme_id",awemeId);
                        crawlerRequestRecord.tagsCreator().bizTags().addCustomKV("aweme_author",keyword);

                        crawlerRequestRecords.add(crawlerRequestRecord);
                    }
                }
            }

            if ("0".equals(statusCode) && "1".equals(hasMore)){
                String maxCursor = accountResultJson.jsonPath($_type + ".max_cursor").get();
                if ("0".equals(maxCursor)){ //有下一页，但是游标出错，重试
                    throw new Exception("dy account next page cursor error:"+maxCursor);
                }
                long rticket = System.currentTimeMillis();
                long ts = rticket/1000;
                String urlParam = String.format(accountUrlParamFormat, maxCursor, keyword, rticket, ts);
                String cookie = page.getRequest().getHeaders().get("Cookie");
                String xgorgon = xGorgon(ts, strToByte(getXGon(urlParam, "", cookie)));
                CrawlerRequestRecord crawlerRequestRecord = CrawlerRequestRecord.builder()
                        .turnPageRequest(crawlerRecord)
                        .httpUrl(accountUrl + urlParam)
                        .httpHeads(page.getRequest().getHeaders())
                        .httpHead("X-Gorgon", xgorgon)
                        .httpHead("X-Khronos", ""+ts)
                        .needWashed(true)
                        .releaseTime(System.currentTimeMillis())
                        .copyBizTags()
                        .build();

                if(lastReaseTime > endTime){ //如果时间超过截止时间，列表页不去重
                    crawlerRequestRecord.tagsCreator().requestTags().removeRequestType(filter);
                }
                crawlerRequestRecords.add(crawlerRequestRecord);
            }
        }catch (Exception e){
            logger.error("dy custom account video request url {} download error, page raw text {}"
                    ,page.getRequest().getUrl(),page.getRawText());
            addCrawlerRecords(crawlerRequestRecords,crawlerRecord);
            crawlerRecord.setNeedWashPage(false); //下载异常，不需要清洗页面
        }

        return crawlerRequestRecords;
    }

    @Override
    public List<CrawlerData> washPage(CrawlerRequestRecord crawlerRecord, HttpPage page) {
        List<CrawlerData> crawlerDataList = new ArrayList<>();

        Json searchJson = new Json(page.getRawText());
        List<String> datas = searchJson.jsonPath($_type + ".aweme_list").all();
        if (datas != null){
            for(String data : datas){
                try {
                    Json dataJson = new Json(data);
                    String aweme_id = dataJson.jsonPath($_type + ".aweme_id").get();
                    String create_time = dataJson.jsonPath($_type + ".create_time").get();
                    String uniqueId = dataJson.jsonPath(".author.unique_id").get();
                    if (StringUtils.isBlank(uniqueId)){
                        uniqueId = dataJson.jsonPath(".author.short_id").get();
                    }
                    CrawlerData crawlerData = CrawlerData.builder()
                            .data(crawlerRecord, page)
                            .dataId(StringUtils.joinWith("-", crawlerRecord.getDomain(), CrawlerEnum.CrawlerDataType.article.enumVal(), aweme_id))
                            .url("")
                            .releaseTime(Long.valueOf(create_time ) * 1000L)
                            .content(data)
                            .requestLabelTag(CrawlerEnum.CrawlerRequestType.result)
                            .requestLabelTag(CrawlerEnum.CrawlerRequestType.filter)
                            .resultLabelTag(CrawlerEnum.CrawlerDataType.article)
                            .build();
                    crawlerData.tagsCreator().bizTags().addCustomKV(Tag_Field_Keword,uniqueId);

                    crawlerDataList.add(crawlerData);
                } catch (Exception e) {
                    logger.error("dy custom account video content data normal,error: {}",e.getMessage());
                }
            }
        }
        return crawlerDataList;
    }

    @Override
    public String domain() {
        return "api-dy";
    }

    @Override
    public void initUrlRegulars() {
        addUrlRegular(accountUrlRegular); //start url--只是为了进入脚本没有实际意义

    }

    @Override
    public boolean crawlerCheck(CrawlerRequestRecord crawlerRecord) {

        String recordSite = crawlerRecord.tagsCreator().bizTags().site();
        if (site.equals(recordSite)){
            return true;
        }
        return false;
    }

    @Override
    public void afterExecute(CrawlerRecordContext context) {

    }

    @Override
    public List<CrawlerRecord> prepareRequest(CrawlerRequestRecord requestRecord, List<CrawlerRequestRecord> supportSourceRecords) {

        List<CrawlerRecord> allItemRecords = new ArrayList<>();

        if (supportSourceRecords == null || supportSourceRecords.size() <1){
            return super.prepareRequest(requestRecord,supportSourceRecords);
        }

        for (CrawlerRequestRecord supportSourceRecord : supportSourceRecords) {
            if (supportSourceRecord.getHttpRequest().getUrl().contains("/v1/meta/douyin/keys?site=api_account")){
                initAllCrawlerRecordByKeyword(requestRecord,supportSourceRecord,allItemRecords);// cookies userAgents 初始完毕后，才能初始record
                break;
            }
        }
        return allItemRecords;
    }

    /**
     * @param requestRecord
     * @param supportSourceRecord
     * @param allItemRecords
     */
    private void initAllCrawlerRecordByKeyword(CrawlerRequestRecord requestRecord,
                                               CrawlerRequestRecord supportSourceRecord,
                                               List<CrawlerRecord> allItemRecords){
        try {
            HttpPage httpPage = supportSourceRecord.getInternalDownloadPage();
            MetaResponse metaResponse = JSON.parseObject(httpPage.getRawText(), MetaResponse.class);
            if (metaResponse.getStatus() == 0 && metaResponse.getContent() != null){
                List<String> contents = (List<String>) metaResponse.getContent();
                long rticket = System.currentTimeMillis();
                long ts = rticket/1000;
//                int count =0;
                for (String content : contents) {
//                    count++;
//                    if (count>1){
//                        break;
//                    }
                    CrawlerDomainKeys crawlerDomainKeys = JSON.parseObject(content, CrawlerDomainKeys.class);
                    String keywordJsonStr = crawlerDomainKeys.getKeyword();
                    Json keywordJson = new Json(keywordJsonStr);
                    String sec_uid = keywordJson.jsonPath($_type + ".sec_uid").get();
                    String unique_id = keywordJson.jsonPath($_type + ".unique_id").get();
                    String urlParam = String.format(accountUrlParamFormat, 0, sec_uid, rticket, ts);
                    String cookie = requestRecord.getHttpRequest().getHeaders().get("Cookie");
                    String xgorgon = xGorgon(ts, strToByte(getXGon(urlParam, "", cookie)));

                    CrawlerRequestRecord crawlerRequestRecord = CrawlerRequestRecord.builder()
                            .turnPageRequest(requestRecord)
                            .httpUrl(accountUrl+urlParam)
                            .httpHeads(requestRecord.getHttpRequest().getHeaders())
                            .httpHead("X-Gorgon", xgorgon)
                            .httpHead("X-Khronos", ""+ts)
                            .releaseTime(System.currentTimeMillis())
                            .copyBizTags()
                            .needWashed(true)
                            .notFilterRecord()
                            .build();

                    crawlerRequestRecord.tagsCreator().bizTags().addKeywords(unique_id);
                    crawlerRequestRecord.tagsCreator().bizTags().addCustomKV("sec_uid",sec_uid);
                    allItemRecords.add(crawlerRequestRecord);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
        }
    }

    private void addCrawlerRecords(List<CrawlerRequestRecord> crawlerRequestRecords,CrawlerRequestRecord crawlerRecord){

        int count = 1;
        CrawlerBusinessTags crawlerBusinessTags = crawlerRecord.tagsCreator().bizTags();
        if (crawlerBusinessTags.hasKVTag("download_retry_count")){
            count = (int) crawlerBusinessTags.getCategoryTag().getKVTag("download_retry_count").getVal();
            if (count >= 20){
                logger.error("dy api account download he number of retries exceeds the limit" +
                        ",request url {},download detail {}",crawlerRecord.getHttpRequest().getUrl());
                return;
            }
        }

        count++;
        crawlerBusinessTags.addCustomKV("download_retry_count",count);

        CrawlerRequestRecord crawlerRequestRecord = CrawlerRequestRecord.builder()
                .turnPageRequest(crawlerRecord)
                .httpRequest(crawlerRecord.getHttpRequest())
                .releaseTime(System.currentTimeMillis())
                .copyBizTags()
                .notFilterRecord()
                .build();
        crawlerRequestRecords.add(crawlerRequestRecord);

    }

    public static Integer[] strToByte(String str){
        int i = 0;
        str = str.toUpperCase();
        char[] tmpStr = str.toCharArray();
        List<Integer> bArr = new ArrayList<>();
        while (i< str.length()){
            char a = tmpStr[i];
            char b = tmpStr[1 + i];
            int c = ((str2int(a) << 4) + str2int(b));
            bArr.add(c);
            i += 2;
        }
        Integer[] intArrays = new Integer[bArr.size()];
        intArrays = bArr.toArray(intArrays);
        return intArrays;
    }

    public static int str2int(char c){
        int odata = 0;
        int tmp = (int) c;
        if (tmp <= (int)('9')){
            odata = odata << 4;
            odata += tmp - (int)('0');
        } else if ((int)('A') <= tmp && tmp <= (int)('F')){
            odata = odata << 4;
            odata += tmp - (int)('A') + 10;
        }
        return odata;
    }

    public static String getXGon(String url,String stub,String cookies) throws UnsupportedEncodingException {
        String sb = "";

        if (url.length() < 1){
            sb = NULL_MD5_STRING;
        } else {
            sb = DigestUtils.md5DigestAsHex(url.getBytes()).toLowerCase();
        }

        if (stub.length() < 1){
            sb += NULL_MD5_STRING;
        } else {
            sb += stub;
        }

        if (cookies.length() < 1){
            sb += NULL_MD5_STRING;
        } else {
            sb += DigestUtils.md5DigestAsHex(cookies.getBytes()).toLowerCase();
        }

        int index = cookies.indexOf("sessionid=");
        if (index == -1){
            sb += NULL_MD5_STRING;
        } else {
            String sessionId = cookies.substring(index+10);
            if (sessionId.contains(";")){
                sessionId = sessionId.substring(0,sessionId.indexOf(";"));
            }
            sb += DigestUtils.md5DigestAsHex(sessionId.getBytes()).toLowerCase();
        }

        return sb;
    }

    public String xGorgon(long timeMillis, Integer[] inputBytes){
        List<String> datas = new ArrayList<>(Arrays.asList("3", "61", "41", "10", "80", "0"));

        List<String> results = new ArrayList<>();
        input(timeMillis,inputBytes,results);
        List<String> initialize = initialize(results);
        List<String> handle = handle(initialize);
        for (String data : handle) {
            datas.add(data);
        }

        String xGorgonStr = "";
        for (String data : datas) {
            if (data.length() > 1){
                xGorgonStr += data;
            }else {
                xGorgonStr += "0";
                xGorgonStr += data;
            }
        }
        return xGorgonStr;
    }

    private void input(long timeMillis, Integer[] inputBytes,List<String> results){
        String tmp;
        for(int i=0;i<4;i++){
            if (inputBytes[i] < 0){
                tmp = Integer.toHexString(inputBytes[i]);
                tmp = tmp.substring(6);
                results.add(tmp);
            } else {
                tmp = Integer.toHexString(inputBytes[i]);
                results.add(tmp);
            }
        }

        for(int i=0;i<4;i++){
            results.add("0");
        }

        for(int i=0;i<4;i++){
            if (inputBytes[i+32] < 0){
                tmp = Integer.toHexString(inputBytes[i+32]);
                tmp = tmp.substring(6);
                results.add(tmp);
            } else {
                tmp = Integer.toHexString(inputBytes[i+32]);
                results.add(tmp);
            }
        }

        for(int i=0;i<4;i++){
            results.add("0");
        }

        String timeHex = Long.toHexString(timeMillis);
        timeHex = timeHex.replace("0x", "");

        for(int i=0;i<4;i++){
            results.add(timeHex.substring(i*2,i*2+2));
        }

        List<String> finalResult = new ArrayList<>();
        for (String result : results) {
            finalResult.add(result.replace("0x", ""));
        }
    }

    private List<String> initialize(List<String> results){
        int hex = 0;
        String[] byteTableArrays = byteTable.split(" ");
        List<String> tmpResults = new ArrayList<>();
        List<String> finalResults = new ArrayList<>();
        for (int i = 0; i < results.size(); i++) {
            Integer hex1;
            if (i == 0){
                hex1 = Integer.valueOf(byteTableArrays[Integer.valueOf(byteTableArrays[0], 16) - 1], 16);
                byteTableArrays[i] = Integer.toHexString(hex1);
            }else {
                int tmp;
                if (i==1){
                    tmp = Integer.valueOf("D6", 16) + Integer.valueOf("28", 16);
                } else {
                    tmp = hex + Integer.valueOf(byteTableArrays[i], 16);
                }
                if (tmp > 256){
                    tmp -= 256;
                }
                hex1 = Integer.valueOf(byteTableArrays[tmp -1],16);
                hex = tmp;
                byteTableArrays[i] = Integer.toHexString(hex1);
            }
            if (hex1 * 2 > 256){
                hex1 = hex1 * 2 - 256;
            } else {
                hex1 = hex1 * 2;
            }

            String hex2 = byteTableArrays[hex1 - 1];
            int result = Integer.valueOf(hex2, 16) ^ Integer.valueOf(results.get(i), 16);
            tmpResults.add(Integer.toHexString(result));
        }

        for (String tmpResult : tmpResults) {
            finalResults.add(tmpResult.replace("0x", ""));
        }

        return finalResults;
    }

    private List<String> handle(List<String> results){
        List<String> finalResults = new ArrayList<>();
        for (int i = 0; i < results.size(); i++) {
            String byte1 = results.get(i);
            if (byte1.length() < 2){
                byte1 += "0";
            } else {
                byte1 = "" + results.get(i).toCharArray()[1] + results.get(i).toCharArray()[0];
            }
            if (i < results.size() - 1){
                byte1 = Integer.toHexString(Integer.valueOf(byte1, 16)
                        ^ Integer.valueOf(results.get(i+1), 16)).replace("0x", "");
            } else {
                byte1 = Integer.toHexString(Integer.valueOf(byte1, 16)
                        ^ Integer.valueOf(finalResults.get(0), 16)).replace("0x", "");
            }
            byte1 = byte1.replace("0x", "");
            int a = (Integer.valueOf(byte1, 16) & Integer.valueOf("AA", 16)) / 2;
            a = Integer.valueOf(abs(a));
            int byte2 = ((Integer.valueOf(byte1, 16) & Integer.valueOf("55", 16)) * 2) | a;
            byte2 = (int)((byte2 & Integer.valueOf("33", 16)) * 4) | (int)((byte2 & Integer.valueOf("cc", 16)) / 4);
            String byte3 = Integer.toHexString(byte2).replace("0x", "");

            if (byte3.length() > 1){
                byte3 = "" + byte3.toCharArray()[1] + byte3.toCharArray()[0];
            }
            else{
                byte3 += "0";
            }
            int byte4 = Integer.valueOf(byte3, 16) ^ Integer.valueOf("FF", 16);
            byte4 = byte4 ^ Integer.valueOf("14", 16);
            finalResults.add(Integer.toHexString(byte4).replace("0x", ""));
        }

        return finalResults;
    }
}
